// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_H_
#define UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_H_

#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "ui/views/view.h"

namespace views {

class SingleSplitViewListener;

// SingleSplitView lays out two views next to each other, either horizontally
// or vertically. A splitter exists between the two views that the user can
// drag around to resize the views.
// SingleSplitViewListener's SplitHandleMoved notification helps to monitor user
// initiated layout changes.
class VIEWS_EXPORT SingleSplitView : public View {
public:
    enum Orientation {
        HORIZONTAL_SPLIT,
        VERTICAL_SPLIT
    };

    static const char kViewClassName[];

    SingleSplitView(View* leading,
        View* trailing,
        Orientation orientation,
        SingleSplitViewListener* listener);

    void Layout() override;
    const char* GetClassName() const override;

    void GetAccessibleState(ui::AXViewState* state) override;

    // SingleSplitView's preferred size is the sum of the preferred widths
    // and the max of the heights.
    gfx::Size GetPreferredSize() const override;

    // Overriden to return a resize cursor when over the divider.
    gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override;

    Orientation orientation() const
    {
        return is_horizontal_ ? HORIZONTAL_SPLIT : VERTICAL_SPLIT;
    }

    void set_orientation(Orientation orientation)
    {
        is_horizontal_ = orientation == HORIZONTAL_SPLIT;
    }

    void set_divider_offset(int divider_offset)
    {
        divider_offset_ = divider_offset;
    }
    int divider_offset() const { return divider_offset_; }

    int GetDividerSize() const;

    void set_resize_disabled(bool resize_disabled)
    {
        resize_disabled_ = resize_disabled;
    }
    bool is_resize_disabled() const { return resize_disabled_; }

    // Sets whether the leading component is resized when the split views size
    // changes. The default is true. A value of false results in the trailing
    // component resizing on a bounds change.
    void set_resize_leading_on_bounds_change(bool resize)
    {
        resize_leading_on_bounds_change_ = resize;
    }

    // Calculates ideal leading and trailing view bounds according to the given
    // split view |bounds|, current divider offset and children visiblity.
    // Does not change children view bounds.
    void CalculateChildrenBounds(const gfx::Rect& bounds,
        gfx::Rect* leading_bounds,
        gfx::Rect* trailing_bounds) const;

    void SetAccessibleName(const base::string16& name);

protected:
    // View overrides.
    bool OnMousePressed(const ui::MouseEvent& event) override;
    bool OnMouseDragged(const ui::MouseEvent& event) override;
    void OnMouseCaptureLost() override;
    void OnBoundsChanged(const gfx::Rect& previous_bounds) override;

private:
    // This test calls OnMouse* functions.
    FRIEND_TEST_ALL_PREFIXES(SingleSplitViewTest, MouseDrag);

    // Returns true if |x| or |y| is over the divider.
    bool IsPointInDivider(const gfx::Point& p);

    // Calculates the new |divider_offset| based on the changes of split view
    // bounds.
    int CalculateDividerOffset(int divider_offset,
        const gfx::Rect& previous_bounds,
        const gfx::Rect& new_bounds) const;

    // Returns divider offset within primary axis size range for given split
    // view |bounds|.
    int NormalizeDividerOffset(int divider_offset, const gfx::Rect& bounds) const;

    // Returns width in case of horizontal split and height otherwise.
    int GetPrimaryAxisSize() const
    {
        return GetPrimaryAxisSize(width(), height());
    }

    int GetPrimaryAxisSize(int h, int v) const
    {
        return is_horizontal_ ? h : v;
    }

    // Used to track drag info.
    struct DragInfo {
        // The initial coordinate of the mouse when the user started the drag.
        int initial_mouse_offset;
        // The initial position of the divider when the user started the drag.
        int initial_divider_offset;
    };

    DragInfo drag_info_;

    // Orientation of the split view.
    bool is_horizontal_;

    // Position of the divider.
    int divider_offset_;

    bool resize_leading_on_bounds_change_;

    // Whether resizing is disabled.
    bool resize_disabled_;

    // Listener to notify about user initiated handle movements. Not owned.
    SingleSplitViewListener* listener_;

    // The accessible name of this view.
    base::string16 accessible_name_;

    DISALLOW_COPY_AND_ASSIGN(SingleSplitView);
};

} // namespace views

#endif // UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_H_
